package com.jzr.open.auth;

import com.jzr.common.util.MD5Util;
import com.jzr.common.util.U;
import com.jzr.open.db1.service.UserService;
import com.jzr.open.db1.vo.MyUserDetailsVo;
import com.jzr.open.util.SpringUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;

/**
 * Created by Shuangyao
 * 22:55 2018/10/15
 */
public class JwtAuthenticationFilter extends OncePerRequestFilter {
    private Logger logger = LoggerFactory.getLogger(JwtAuthenticationFilter.class);


    @Autowired
    private UserService userService;

    public JwtAuthenticationFilter(){
        userService = SpringUtil.getBean(UserService.class);
    }

    //1.从每个请求header获取token
    //2.调用前面写的validateToken方法对token进行合法性验证
    //3.解析得到username，并从database取出用户相关信息权限
    //4.把用户信息以UserDetail形式放进SecurityContext以备整个请求过程使用。
    // （例如哪里需要判断用户权限是否足够时可以直接从SecurityContext取出去check
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
        ServletRequest requestWrapper = null;
        String signs = request.getHeader("sign");
        String timestamp = request.getHeader("timestamp");
        int code = HttpServletResponse.SC_FORBIDDEN;
        String msg = "无权访问";
        try {
            if (U.isNotBlank(signs) && U.isNotBlank(timestamp)) {
                SortedMap<String,String> sortedMap = new TreeMap<>();
                for(String key : request.getParameterMap().keySet()){
                    sortedMap.put(key,request.getParameter(key));
                }
                StringBuilder sbParam = new StringBuilder();
                for(Map.Entry<String,String> entry : sortedMap.entrySet()){
                    if(U.isNotBlank(entry.getValue())) {
                        sbParam.append(entry.getKey());
                        sbParam.append("=");
                        sbParam.append(entry.getValue());
                    }
                }
                if(request instanceof HttpServletRequest) {
                    //为了其他方法多次调用request.getBody()方法
                    requestWrapper = new RequestWrapper(request);
                }

                String[] signArray = signs.split(" ");
                if(signArray == null || signArray.length<2){
                    code = HttpServletResponse.SC_FORBIDDEN;
                    msg = "sign格式有误";
                    writeResult(request,response,code,msg);
                    return;
                }
                String userCode = signArray[0];
                String sign = signArray[1];
                if (U.isBlank(sign)) {
                    code = HttpServletResponse.SC_FORBIDDEN;
                    msg = "验证失败,sign不存在";
                    writeResult(request,response,code,msg);
                    return;
                }
                try {
                    MyUserDetailsVo myUserDetailsVo = userService.getUserDetailByUserCode(userCode);
                    if (myUserDetailsVo == null) {
                        code = HttpServletResponse.SC_FORBIDDEN;
                        msg = "验证失败,apiKey不存在";
                        writeResult(request,response,code,msg);
                        return;
                    }
                    String token = myUserDetailsVo.getToken();
                    if (U.isBlank(token)) {
                        code = HttpServletResponse.SC_FORBIDDEN;
                        msg = "验证失败,token不存在";
                        writeResult(request,response,code,msg);
                        return;
                    }
                    String sign2 = MD5Util.MD5(sbParam.toString() + ((RequestWrapper) requestWrapper).getBody() + token + timestamp);
                    if (!sign.equalsIgnoreCase(sign2)) {
                        code = HttpServletResponse.SC_FORBIDDEN;
                        msg = "验证失败,sign值有误";
                        writeResult(request,response,code,msg);
                        return;
                    }
                    Authentication authentication = new UsernamePasswordAuthenticationToken(
                            myUserDetailsVo, null, myUserDetailsVo.getAuthorities());
                    SecurityContextHolder.getContext().setAuthentication(authentication);

                } catch (Exception e) {
                    e.printStackTrace();
                    code = HttpServletResponse.SC_FORBIDDEN;
                    msg = "验证失败," + e.getMessage();
                    writeResult(request,response,code,msg);
                    return;
                }
            }

            if(requestWrapper == null) {
                super.doFilter(request, response, filterChain);
            } else {
                super.doFilter(requestWrapper, response, filterChain);
            }

        }catch (Exception e){
            e.printStackTrace();
            code = HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
            msg = "系统错误，请联系技术";
            writeResult(request,response,code,msg);
            return;
        }
    }

    private void writeResult(HttpServletRequest request, HttpServletResponse response,int code, String msg) throws IOException {
        SecurityContextHolder.getContext().setAuthentication(null);
        request.setAttribute("vStatus",code);
        request.setAttribute("vMessage",msg);
        response.setStatus(code);
        response.setContentType("application/json;charset=utf-8");
        PrintWriter out = response.getWriter();
        out.write("{\"code\":" + code + ",\"status\":" + code + ",\"msg\":\"" + msg + "\"" + ",\"message\":\"" + msg + "\"}");
        out.flush();
        out.close();
    }
}
